/**
* Copyright (c) 2011-2020, hubin (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.alinesno.cloud.compoment.generate.generator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alinesno.cloud.compoment.generate.generator.config.ConstVal;
import com.alinesno.cloud.compoment.generate.generator.config.DataSourceConfig;
import com.alinesno.cloud.compoment.generate.generator.config.FileOutConfig;
import com.alinesno.cloud.compoment.generate.generator.config.GlobalConfig;
import com.alinesno.cloud.compoment.generate.generator.config.PackageConfig;
import com.alinesno.cloud.compoment.generate.generator.config.StrategyConfig;
import com.alinesno.cloud.compoment.generate.generator.config.TemplateConfig;
import com.alinesno.cloud.compoment.generate.generator.config.builder.ConfigBuilder;
import com.alinesno.cloud.compoment.generate.generator.config.po.TableField;
import com.alinesno.cloud.compoment.generate.generator.config.po.TableInfo;
import com.alinesno.cloud.compoment.generate.generator.config.rules.NamingStrategy;

/**
 * 文件生成
 * @author WeiXiaoJin
 * @since 2019年9月27日 下午7:48:56
 */
public class AutoGenerator {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());  

    protected ConfigBuilder config;
    protected InjectionConfig injectionConfig;
    /**
     * 数据源配置
     */
    private DataSourceConfig dataSource;
    /**
     * 数据库表配置
     */
    private StrategyConfig strategy;
    /**
     * 包 相关配置
     */
    private PackageConfig packageInfo;
    /**
     * 模板 相关配置
     */
    private TemplateConfig template;
    /**entity path
     * 全局 相关配置
     */
    private GlobalConfig globalConfig;
    /**
     * velocity引擎
     */
    private VelocityEngine engine;

    @SuppressWarnings("unused")
	private String abcPath ; 
    
    
    public AutoGenerator(String abcPath) {
		this.abcPath = abcPath;
	}

	/**
     * 生成代码
     */
    public void execute(PackageConfig pp) {
        logger.debug("==========================准备生成文件...==========================");
        // 初始化配置
        initConfig();
        // 创建输出文件路径
        mkdirs(config.getPathInfo());
        
        // 获取上下文
        Map<String, VelocityContext> ctxData = analyzeData(config);
        
        for (Map.Entry<String, VelocityContext> ctx : ctxData.entrySet()) {
            batchOutput(ctx.getKey(), ctx.getValue());
        }
        
        // 循环生成文件
        String basePackagePath = config.getPackageInfo().get(ConstVal.BOOT); 
        
        // 生成boot启动类
        createStartBoot(basePackagePath , pp) ; 
        
        // 打开输出目录
        if (config.getGlobalConfig().isOpen()) {
            try {
                String osName = System.getProperty("os.name");
                if (osName != null) {
                    if (osName.contains("Mac")) {
                        Runtime.getRuntime().exec("open " + config.getGlobalConfig().getOutputDir());
                    } else if (osName.contains("Windows")) {
                        Runtime.getRuntime().exec("cmd /c start " + config.getGlobalConfig().getOutputDir());
                    } else {
                        logger.debug("文件输出目录:" + config.getGlobalConfig().getOutputDir());
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        logger.debug("==========================文件生成完成！！！==========================");
    }

    /**
     * 生成springboot启动类
     * @param basePackagePath
     * @param p
     * @throws IOException 
     */
    private void createStartBoot(String basePackagePath, PackageConfig p) {
		String springBootFile = String.format(basePackagePath + File.separator);
		springBootFile = springBootFile.replaceAll("\\.", "\\" + File.separator); 

		String abcPath = p.getAbcPath() ; // PropertiesUtil.getString("code.outputdir") ;
		String abcCommonPath = abcPath + (abcPath.endsWith("\\")?"":File.separator) ; 
		
		String bootPath =  abcCommonPath + ConstVal.COMPILER_PATH_JAVA + springBootFile  + config.getBootPrefix() + "Application" + ConstVal.JAVA_SUFFIX; 
		String applicationYmlPath =  abcCommonPath + ConstVal.COMPILER_PATH_RESOUCE +  "application-dev" + ConstVal.YML_SUFFIX; 
		String applicationLocalYmlPath =  abcCommonPath + ConstVal.COMPILER_PATH_RESOUCE +  "application-hub" + ConstVal.YML_SUFFIX; 
		String pomXmlPath =  abcCommonPath +  "pom" + ConstVal.XML_SUFFIX; 
		String k8sPath =  abcCommonPath +  "k8s-dev" + ConstVal.YML_SUFFIX ; 
		String gitignorePath =  abcCommonPath +  ".gitignore" ; 
		
		// dubbo_xml
		String dubboProviderXmlPath =  abcCommonPath + ConstVal.COMPILER_PATH_DUBBO_RESOUCE +  "dubbo-provider" + ConstVal.XML_SUFFIX; 
		String dubboConsumerXmlPath =  abcCommonPath + ConstVal.COMPILER_PATH_DUBBO_RESOUCE +  "dubbo-consumer" + ConstVal.XML_SUFFIX; 
		
		// k8s.yml
		
		logger.debug("abcPath:{} , bootPath:{} , applicationYml:{} , pomXml:{}" , abcPath , bootPath , applicationYmlPath , pomXmlPath);
		
		VelocityEngine velocity = getVelocityEngine();
		Template template = velocity.getTemplate("templates/"+ConstVal.TEMPLATE_BOOT , ConstVal.UTF8);
		File file = new File(abcPath);
		
		if (!file.getParentFile().exists()) {
			// 如果文件所在的目录不存在，则创建目录
			if (!file.getParentFile().mkdirs()) {
				logger.debug("创建文件所在的目录失败!");
				return;
			}
		}
		
		// 生成 yml 和 pom.xml 文件 
        String springApplicationName = p.getSpringApplicationName() ; 
        String serverPort  = p.getServerPort() ; 
        String databaseDriverClass = p.getDatabaseDriverClass() ; 
        String databaseUrl = p.getDatabaseUrl() ; 
        String databaseUsername = p.getDatabaseUsername() ; 
        String databasePassword = p.getDatabasePassword() ; 
       
        String articleId = config.getArticleId() ; 
        String groupId = config.getGroupId() ; 
        String bootPrefix = config.getBootPrefix() ; 
        
        logger.debug("applicationName:{} , serverPort:{}" , springApplicationName , serverPort);
		
		try {
			VelocityContext ctx = new VelocityContext() ; 
			String createDate = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:MM:SS");
			ctx.put("basePackagePath", basePackagePath) ; 
			ctx.put("author", PropertiesUtil.getString("code.author")) ; 
			ctx.put("createDate", createDate) ; 
			
			ctx.put("springApplicationName", springApplicationName) ; 
            ctx.put("serverPort", serverPort) ; 
            ctx.put("databaseDriverClass", databaseDriverClass) ; 
            ctx.put("databaseUrl", databaseUrl.trim()) ; 
            ctx.put("databaseUsername", databaseUsername) ; 
            ctx.put("databasePassword", databasePassword) ; 
            
            ctx.put("artifactId", articleId) ; 
            ctx.put("groupId", groupId) ; 
            ctx.put("bootPrefix", bootPrefix) ; 
            
            logger.debug("database url :{}" , databaseUrl);
			
			// boot 
			FileOutputStream fos = new FileOutputStream(bootPath);
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, ConstVal.UTF8));
			template.merge(ctx, writer);
			writer.close();
			
			// application.dev.yml 
			Template template1 = velocity.getTemplate("templates/"+ConstVal.APPLICATION_YML_PATH , ConstVal.UTF8);
			BufferedWriter writer1 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(applicationYmlPath), ConstVal.UTF8));
			template1.merge(ctx, writer1);
			writer1.close();
			
			// application.hub.yml 
			Template templateLocal = velocity.getTemplate("templates/"+ConstVal.APPLICATION_HUB_YML_PATH , ConstVal.UTF8);
			BufferedWriter writerLocal = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(applicationLocalYmlPath), ConstVal.UTF8));
			templateLocal.merge(ctx, writerLocal);
			writerLocal.close();
			
			// dubbo consumer.xml
			FileUtils.forceMkdir(new File(dubboConsumerXmlPath).getParentFile());
			Template consumerXML = velocity.getTemplate("templates/"+ConstVal.DUBBO_CONSUMR_XML_PATH, ConstVal.UTF8);
			BufferedWriter writerConsumerXML = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dubboConsumerXmlPath), ConstVal.UTF8));
			consumerXML.merge(ctx, writerConsumerXML);
			writerConsumerXML.close();
			
			FileUtils.forceMkdir(new File(dubboProviderXmlPath).getParentFile());
			Template providerXML = velocity.getTemplate("templates/"+ConstVal.DUBBO_PROVIDER_XML_PATH, ConstVal.UTF8);
			BufferedWriter writerProviderXML = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dubboProviderXmlPath), ConstVal.UTF8));
			providerXML.merge(ctx, writerProviderXML);
			writerProviderXML.close();
			
			// k8s.yml
			Template k8sTemplate = velocity.getTemplate("templates/"+ConstVal.K8S_YML_PATH , ConstVal.UTF8);
			FileOutputStream fosK8S = new FileOutputStream(k8sPath);
			BufferedWriter writerK8S = new BufferedWriter(new OutputStreamWriter(fosK8S, ConstVal.UTF8));
			k8sTemplate.merge(ctx, writerK8S);
			writerK8S.close();
			
			// .gitignore 
			Template gitignore = velocity.getTemplate("templates/"+ConstVal.GIT_IGNORE_PATH , ConstVal.UTF8);
			BufferedWriter gitignoreLocal = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(gitignorePath), ConstVal.UTF8));
			gitignore.merge(ctx, gitignoreLocal);
			gitignoreLocal.close();
			
			// pom.xml 
			Template template2 = velocity.getTemplate("templates/"+ConstVal.POM_XML_PATH , ConstVal.UTF8);
			FileOutputStream fos2 = new FileOutputStream(pomXmlPath);
			BufferedWriter writer2 = new BufferedWriter(new OutputStreamWriter(fos2, ConstVal.UTF8));
			template2.merge(ctx, writer2);
			writer2.close();
			
		} catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException | IOException e) {
			e.printStackTrace();
		}
		
	}

	/**
     * <p>
     * 开放表信息、预留子类重写
     * </p>
     *
     * @param config 配置信息
     * @return
     */
    protected List<TableInfo> getAllTableInfoList(ConfigBuilder config) {
        return config.getTableInfoList();
    }

    /**
     * <p>
     * 分析数据
     * </p>
     *
     * @param config 总配置信息
     * @return 解析数据结果集
     */
    private Map<String, VelocityContext> analyzeData(ConfigBuilder config) {
    	
        List<TableInfo> tableList = this.getAllTableInfoList(config);
        Map<String, String> packageInfo = config.getPackageInfo();
        Map<String, VelocityContext> ctxData = new HashMap<>();
        
        // 持久层
        String superRepositoryClass = getSuperClassName(config.getSuperRepositoryClass());
        String idKeyType = config.getIdKeyType() ;
        String superEntityClass = getSuperClassName(config.getSuperEntityClass());
        String superMapperClass = getSuperClassName(config.getSuperMapperClass());
       
        // 服务层
        String superServiceClass = getSuperClassName(config.getSuperServiceClass());
        String superServiceImplClass = getSuperClassName(config.getSuperServiceImplClass());
     
        // Feigin
        String superFeiginDtoClass = getSuperClassName(config.getSuperFeiginDtoClass());
        
        // boot 
        String bootPrefix = config.getBootPrefix();

        // 生成 yml 和 pom.xml 文件 
        String springApplicationName = config.getSpringApplicationName() ; 
        String serverPort  = config.getServerPort() ; 
        String databaseDriverClass = config.getDatabaseDriverClass() ; 
        String databaseUrl = config.getDatabaseUrl() ; 
        String databaseUsername = config.getDatabaseUsername() ; 
        String databasePassword = config.getDatabasePassword() ; 
       
        String articleId = config.getArticleId() ; 
        String groupId = config.getGroupId() ; 
 
        // 前端
        String superControllerClass = getSuperClassName(config.getSuperControllerClass());
        String superRestControllerClass = getSuperClassName(config.getSuperRestControllerClass());
        
        String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        
        VelocityContext ctx;
        for (TableInfo tableInfo : tableList) {
            ctx = new VelocityContext();
            if (null != injectionConfig) {
                /**
                 * 注入自定义配置
                 */
                injectionConfig.initMap();
                ctx.put("cfg", injectionConfig.getMap());
            }
            if (tableInfo.isConvert()) {
                // 表注解
                tableInfo.setImportPackages("javax.persistence.Table");
                tableInfo.setImportPackages("javax.persistence.Entity");
            }
            if (tableInfo.isLogicDelete(config.getStrategyConfig().getLogicDeleteFieldName())) {
                // 逻辑删除注解
                tableInfo.setImportPackages("com.baomidou.mybatisplus.annotations.TableLogic");
            }
            if (StringUtils.isNotEmpty(config.getStrategyConfig().getVersionFieldName())) {
                // 乐观锁注解
                tableInfo.setImportPackages("com.baomidou.mybatisplus.annotations.Version");
            }
            if (tableInfo.hasDate()) {
                // 包含日期  
                tableInfo.setImportPackages("com.alibaba.fastjson.annotation.JSONField");
            }
            if (StringUtils.isNotEmpty(config.getSuperEntityClass())) {
                // 父实体
                tableInfo.setImportPackages(config.getSuperEntityClass());
            } else {
                tableInfo.setImportPackages("java.io.Serializable");
            }
            // Boolean类型is前缀处理
            if (config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix()) {
                for (TableField field : tableInfo.getFields()) {
                    if (field.getPropertyType().equalsIgnoreCase("boolean")) {
                        if (field.getPropertyName().startsWith("is")) {
                            field.setPropertyName(config.getStrategyConfig(),
                                    StringUtils.removePrefixAfterPrefixToLower(field.getPropertyName(), 2));
                        }
                    }
                }
            }
            
            // RequestMapping 连字符风格 user-info
            // 修改成去Entity类标签模式
            if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
                ctx.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle());
                String entityPath = tableInfo.getEntityPath().replaceAll(ConstVal.ENTITY, "") ;
                ctx.put("controllerMappingHyphen", entityPath);
            }
            
            String serviceImplName = tableInfo.getServiceName() ; 

            ctx.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle());
            ctx.put("package", packageInfo);
            ctx.put("author", config.getGlobalConfig().getAuthor());
            ctx.put("logicDeleteFieldName", config.getStrategyConfig().getLogicDeleteFieldName());
            ctx.put("versionFieldName", config.getStrategyConfig().getVersionFieldName());
            ctx.put("activeRecord", config.getGlobalConfig().isActiveRecord());
            ctx.put("date", date);
            ctx.put("table", tableInfo);
            ctx.put("enableCache", config.getGlobalConfig().isEnableCache());
            ctx.put("baseResultMap", config.getGlobalConfig().isBaseResultMap());
            ctx.put("baseColumnList", config.getGlobalConfig().isBaseColumnList());
            ctx.put("entity", tableInfo.getEntityName());
            ctx.put("lowerServiceName", StringUtils.removePrefixAfterPrefixToLower(tableInfo.getServiceName(), 1));
            ctx.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant());
            ctx.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel());
            ctx.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel());
            ctx.put("entityBooleanColumnRemoveIsPrefix", config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
            ctx.put("superEntityClass", superEntityClass);
            ctx.put("superMapperClassPackage", config.getSuperMapperClass());
            ctx.put("superMapperClass", superMapperClass);
            ctx.put("superServiceClassPackage", config.getSuperServiceClass());
            ctx.put("superServiceClass", superServiceClass);
            ctx.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
            ctx.put("superServiceImplClass", superServiceImplClass);
            ctx.put("superControllerClassPackage", config.getSuperControllerClass());
            ctx.put("superControllerClass", superControllerClass);
            
            // 添加 rest controller 支持
            ctx.put("superRestControllerClassPackage", config.getSuperRestControllerClass());
            ctx.put("superRestControllerClass", superRestControllerClass);
           
            // 添加Feigin支持
            ctx.put("superFeiginDtoClass", superFeiginDtoClass);
            ctx.put("superFeiginDtoClassPackage", config.getSuperFeiginDtoClass());
            ctx.put("feiginServer", config.getFeiginServer());
            
            //添加spring data jpa支持
            ctx.put("superRepositoryClassPackage", config.getSuperRepositoryClass());
            ctx.put("superRepositoryName", superRepositoryClass);
            ctx.put("idKeyType", idKeyType) ; 
            
            // 添加springboot支持
            ctx.put("bootPrefix", bootPrefix) ; 
            
            // yml and pom.xml
            ctx.put("springApplicationName", springApplicationName) ; 
            ctx.put("serverPort", serverPort) ; 
            ctx.put("databaseDriverClass", databaseDriverClass) ; 
            ctx.put("databaseUrl", databaseUrl) ; 
            ctx.put("databaseUsername", databaseUsername) ; 
            ctx.put("databasePassword", databasePassword) ; 
            
            ctx.put("articleId", articleId) ; 
            ctx.put("groupId", groupId) ; 
            
            //设置自动注入对象名称
            ctx.put("serviceAutoImpl", NamingStrategy.capitalLowerFirst(serviceImplName));
            
            ctxData.put(tableInfo.getEntityName(), ctx);
        }
        return ctxData;
    }

    /**
     * <p>
     * 获取类名
     * </p>
     *
     * @param classPath
     * @return
     */
    private String getSuperClassName(String classPath) {
        if (StringUtils.isEmpty(classPath))
            return null;
        return classPath.substring(classPath.lastIndexOf(".") + 1);
    }

    /**
     * <p>
     * 处理输出目录
     * </p>
     *
     * @param pathInfo 路径信息
     */
    private void mkdirs(Map<String, String> pathInfo) {
        for (Map.Entry<String, String> entry : pathInfo.entrySet()) {
            File dir = new File(entry.getValue());
            if (!dir.exists()) {
                boolean result = dir.mkdirs();
                if (result) {
                    logger.debug("创建目录： [" + entry.getValue() + "]");
                }
            }
        }
    }

    /**
     * <p>
     * 合成上下文与模板
     * </p>
     * 
     * @param context vm上下文
     * @param entityName
     */
    private void batchOutput(String entityName, VelocityContext context) {
        try {
            TableInfo tableInfo = (TableInfo) context.get("table");
            Map<String, String> pathInfo = config.getPathInfo();
           
            // create java
            String entityFile = String.format((pathInfo.get(ConstVal.ENTITY_PATH) + ConstVal.ENTITY_NAME), entityName);
            String serviceFile = String.format((pathInfo.get(ConstVal.SERIVCE_PATH) + File.separator + tableInfo.getServiceName() + ConstVal.JAVA_SUFFIX), entityName);
            String implFile = String.format((pathInfo.get(ConstVal.SERVICEIMPL_PATH) + File.separator + tableInfo.getServiceImplName() + ConstVal.JAVA_SUFFIX), entityName);
            String controllerFile = String.format((pathInfo.get(ConstVal.CONTROLLER_PATH) + File.separator + tableInfo.getControllerName() + ConstVal.JAVA_SUFFIX), entityName);
          
            // create data jpa
            String repositoryFile = String.format((pathInfo.get(ConstVal.REPOSITORY_PATH) + File.separator + tableInfo.getRepositoryName() + ConstVal.JAVA_SUFFIX), entityName);
            
            // create rest controller
//            String restControllerFile = String.format((pathInfo.get(ConstVal.REST_CONTROLLER_PATH) + File.separator + tableInfo.getRestControllerName() + ConstVal.JAVA_SUFFIX), entityName);
            
            // create feigin 
//            String feiginFile = String.format((pathInfo.get(ConstVal.FEIGIN_API_PATH) + File.separator + tableInfo.getFeiginName() + ConstVal.JAVA_SUFFIX), entityName);
//            String feiginDtoFile = String.format((pathInfo.get(ConstVal.FEIGIN_DTO_PATH) + File.separator + tableInfo.getFeiginDtoName() + ConstVal.JAVA_SUFFIX), entityName);
            
            //create jsp
            String pageListFile = String.format((pathInfo.get(ConstVal.PAGE_LIST_PATH) + File.separator + tableInfo.getPagelistPageName()), entityName);
            String pageAddFile = String.format((pathInfo.get(ConstVal.PAGE_ADD_PATH) + File.separator + tableInfo.getPageAddPageName()), entityName);
            String pageModifyFile = String.format((pathInfo.get(ConstVal.PAGE_MODIFY_PATH) + File.separator + tableInfo.getPageModifyPageName()), entityName);
            String pageDetailFile = String.format((pathInfo.get(ConstVal.PAGE_DETAIL_PATH) + File.separator + tableInfo.getPageDetailPageName()), entityName);

            TemplateConfig template = config.getTemplate();

            // 根据override标识来判断是否需要创建文件
            if (isCreate(entityFile)) {
			    logger.debug("entity file = {}" , entityFile);
                vmToFile(context, template.getEntity(), entityFile);
            }
           
            if (isCreate(serviceFile)) {
                vmToFile(context, template.getService(), serviceFile);
            }
            if (isCreate(implFile)) {
                vmToFile(context, template.getServiceImpl(), implFile);
            }
            if (isCreate(controllerFile)) {
                vmToFile(context, template.getController(), controllerFile);
            }
            
            // 生成Repository文件 
            if (isCreate(repositoryFile)) {
                vmToFile(context, template.getRepository() , repositoryFile);
            }
            
//            // 生成 restController文件 
//            if (isCreate(restControllerFile)) {
//                vmToFile(context, template.getRestController() , restControllerFile);
//            }
//            
//            // 生成 feigin文件 
//            if (isCreate(feiginFile)) {
//                vmToFile(context, template.getFeigin() , feiginFile);
//            }
//            
//            // 生成 feigin文件 
//            if (isCreate(feiginDtoFile)) {
//                vmToFile(context, template.getFeiginDto() , feiginDtoFile);
//            }
           
            //create jsp 
            if(isCreate(pageListFile)) {
                vmToFile(context, template.getPageList(), pageListFile);
            }
            if(isCreate(pageAddFile)) {
                vmToFile(context, template.getPageAdd(), pageAddFile);
            }
            if(isCreate(pageModifyFile)) {
                vmToFile(context, template.getPageModify(), pageModifyFile);
            }
            if(isCreate(pageDetailFile)) {
                vmToFile(context, template.getPageDetail(), pageDetailFile);
            }
            
            if (injectionConfig != null) {
                /**
                 * 输出自定义文件内容
                 */
                List<FileOutConfig> focList = injectionConfig.getFileOutConfigList();
                if (CollectionUtils.isNotEmpty(focList)) {
                    for (FileOutConfig foc : focList) {
                        // 判断自定义文件是否存在
                        if (isCreate(foc.outputFile(tableInfo))) {
                            vmToFile(context, foc.getTemplatePath(), foc.outputFile(tableInfo));
                        }
                    }
                }
            }

        } catch (IOException e) {
            logger.error("无法创建文件，请检查配置信息！", e);
        }
    }

    /**
     * <p>
     * 将模板转化成为文件
     * </p>
     *
     * @param context      内容对象
     * @param templatePath 模板文件
     * @param outputFile   文件生成的目录
     */
    private void vmToFile(VelocityContext context, String templatePath, String outputFile) throws IOException {
        if (StringUtils.isEmpty(templatePath)) {
            return;
        }
       
        VelocityEngine velocity = getVelocityEngine();
        Template template = velocity.getTemplate("templates/"+templatePath, ConstVal.UTF8);

        File file = new File(outputFile);
        if (!file.getParentFile().exists()) {
            // 如果文件所在的目录不存在，则创建目录
            if (!file.getParentFile().mkdirs()) {
                logger.debug("创建文件所在的目录失败!");
                return;
            }
        }
        FileOutputStream fos = new FileOutputStream(outputFile);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, ConstVal.UTF8));
        template.merge(context, writer);
        writer.close();
        // logger.debug("模板:" + templatePath + ";  文件:" + outputFile);
    }

    /**
     * 设置模版引擎，主要指向获取模版路径
     */
    private VelocityEngine getVelocityEngine() {
        if (engine == null) {
            Properties p = new Properties();
            p.setProperty(ConstVal.VM_LOADPATH_KEY, ConstVal.VM_LOADPATH_VALUE);
    		p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
            p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
            p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
            p.setProperty("file.resource.loader.unicode", "true");
            engine = new VelocityEngine(p);
        }
        return engine;
    }

    /**
     * 检测文件是否存在
     *
     * @return 是否
     */
    private boolean isCreate(String filePath) {
        File file = new File(filePath);
        return !file.exists() || config.getGlobalConfig().isFileOverride();
    }

    // ==================================  相关配置  ==================================

    /**
     * 初始化配置
     */
    protected void initConfig() {
        if (null == config) {
            config = new ConfigBuilder(packageInfo, dataSource, strategy, template, globalConfig);
            if (null != injectionConfig) {
                injectionConfig.setConfig(config);
            }
        }
    }

    public DataSourceConfig getDataSource() {
        return dataSource;
    }

    public AutoGenerator setDataSource(DataSourceConfig dataSource) {
        this.dataSource = dataSource;
        return this;
    }

    public StrategyConfig getStrategy() {
        return strategy;
    }

    public AutoGenerator setStrategy(StrategyConfig strategy) {
        this.strategy = strategy;
        return this;
    }

    public PackageConfig getPackageInfo() {
        return packageInfo;
    }

    public AutoGenerator setPackageInfo(PackageConfig packageInfo) {
        this.packageInfo = packageInfo;
        return this;
    }

    public TemplateConfig getTemplate() {
        return template;
    }

    public AutoGenerator setTemplate(TemplateConfig template) {
        this.template = template;
        return this;
    }

    public ConfigBuilder getConfig() {
        return config;
    }

    public AutoGenerator setConfig(ConfigBuilder config) {
        this.config = config;
        return this;
    }

    public GlobalConfig getGlobalConfig() {
        return globalConfig;
    }

    public AutoGenerator setGlobalConfig(GlobalConfig globalConfig) {
        this.globalConfig = globalConfig;
        return this;
    }

    public InjectionConfig getCfg() {
        return injectionConfig;
    }

    public AutoGenerator setCfg(InjectionConfig injectionConfig) {
        this.injectionConfig = injectionConfig;
        return this;
    }
}
